package dk.eclipse.android.badassapp.directions;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.zip.GZIPInputStream;

import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;
import com.google.android.maps.MyLocationOverlay;
import com.google.android.maps.Overlay;
import com.google.android.maps.OverlayItem;

import dk.eclipse.android.badassapp.R;
import dk.eclipse.android.badassapp.contact.ContactsHelper;
import dk.eclipse.android.badassapp.distance.MapRectangle;
import dk.eclipse.android.badassapp.distance.ObjectOnMap;
import dk.eclipse.android.badassapp.helper.LocationHelper;
import dk.eclipse.android.badassapp.helper.NotificationHelper;
import dk.eclipse.android.badassapp.preferences.PreferenceHelper;

public class DirectionsActivity extends MapActivity {
	/**
	 * Shown in every log line as identification.
	 */
	private static final String LOG_TAG = DirectionsActivity.class
			.getSimpleName();

	private MapView mapView;
	private int currentRouteIndex = 0;
	private boolean directionsLoaded = false;

	/** Distance covered. **/
	private int totalDistance;
	private List<Route> routes = new ArrayList<Route>();

	private TextView distanceTextView;
	private TextView instructionTextView;
	private TextView latitudeTextView;
	private TextView longitudeTextView;

	private GeoPoint myGeoPoint;
	private GeoPoint targetGeoPoint;

	private double myLatitude;
	private double myLongitude;
	private double targetLatitude;
	private double targetLongitude;

	private MyLocationOverlay me = null;

	private DirectionsLoaderTask directionsLoader;
	
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.directions);
		
		LocationHelper.setContext(this);

		myLatitude = PreferenceHelper.getMyLatitude(this);
		myLongitude = PreferenceHelper.getMyLongitude(this);
		targetLatitude = PreferenceHelper.getTargetLatitude(this);
		targetLongitude = PreferenceHelper.getTargetLongitude(this);

		mapView = (MapView) findViewById(R.id.directionsMapView);

		myGeoPoint = new GeoPoint((int) (myLatitude * 1E6),
				(int) (myLongitude * 1E6));
		targetGeoPoint = new GeoPoint((int) (targetLatitude * 1E6),
				(int) (targetLongitude * 1E6));

		distanceTextView = (TextView) findViewById(R.id.routeDistanceTextView);
		instructionTextView = (TextView) findViewById(R.id.routeInstructionTextView);
		latitudeTextView = (TextView) findViewById(R.id.routeLatitudeTextView);
		longitudeTextView = (TextView) findViewById(R.id.routeLongitudeTextView);

		Button previousButton = (Button) findViewById(R.id.routePreviousButton);
		previousButton.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {
				if(directionsLoaded) {
					previousRouteInfo();
				} else {
					NotificationHelper.sendToast(DirectionsActivity.this, "No directions data loaded", Toast.LENGTH_LONG);
				}
			}
		});
		Button nextButton = (Button) findViewById(R.id.routeNextButton);
		nextButton.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {
				if(directionsLoaded) {
					nextRouteInfo();
				} else {
					NotificationHelper.sendToast(DirectionsActivity.this, "No directions data loaded", Toast.LENGTH_LONG);
				}
			}
		});

		me = new MyLocationOverlay(this, mapView);
		mapView.getOverlays().add(me);

		directionsLoader = new DirectionsLoaderTask();
		directionsLoader
				.execute("http://maps.google.com/maps/api/directions/json?origin="
						+ myLatitude
						+ ","
						+ myLongitude
						+ "&destination="
						+ targetLatitude
						+ ","
						+ targetLongitude
						+ "&mode=driving&sensor=false");
		
		
		Collection<ObjectOnMap> values = new ArrayList<ObjectOnMap>();
		values.add(new ObjectOnMap(myLatitude, myLongitude, "Me", ""));
		values.add(new ObjectOnMap(targetLatitude, targetLongitude, "Target", ""));
		MapRectangle mapRect = new MapRectangle(values );
		centerMapBetweenMeAndTarget(mapRect);

	}

	protected void previousRouteInfo() {
		if (currentRouteIndex > 0) {
			currentRouteIndex--;
		}
		setRouteInfo(currentRouteIndex);
	}

	protected void nextRouteInfo() {
		if (currentRouteIndex < routes.size() - 1) {
			currentRouteIndex++;
		}
		setRouteInfo(currentRouteIndex);

	}

	private void setRouteInfo(int index) {

		final Route route = routes.get(index);

		runOnUiThread( new Runnable() {
			
			@Override
			public void run() {
				distanceTextView.setText(route.distanceText);
				instructionTextView.setText(route.instruction);
				latitudeTextView.setText(String.valueOf(route.latitude));
				longitudeTextView.setText(String.valueOf(route.longitude));
				
			}
		});

	}

	@Override
	protected boolean isRouteDisplayed() {
		return false;
	}

	/**
	 * @author Ronni
	 * 
	 */
	public class Route {
		double latitude;
		double longitude;

		String instruction;
		String distanceText;
		int distance;

		/**
		 * @param latitude
		 * @param longitude
		 * @param instruction
		 * @param distanceText
		 * @param distance
		 */
		public Route(double latitude, double longitude, String instruction,
				String distanceText, int distance) {
			super();
			this.latitude = latitude;
			this.longitude = longitude;
			this.instruction = instruction;
			this.distanceText = distanceText;
			this.distance = distance;
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see java.lang.Object#toString()
		 */
		@Override
		public String toString() {
			StringBuilder sb = new StringBuilder();
			sb.append(latitude);
			sb.append(", ");
			sb.append(longitude);
			sb.append(" ");
			sb.append(instruction);
			sb.append(" ");
			sb.append(distanceText);

			return sb.toString();
		}

	}

	/**
	 * @author Ronni
	 * 
	 */
	private class DirectionsLoaderTask extends
			AsyncTask<String, List<Route>, Integer> {

		private static final String HTTP_HEADER_ACCEPT_ENCODING = "Accept-Encoding";
		private static final String HTTP_HEADER_CONTENT_ENCODING = "Content-Encoding";
		private static final String CONTENT_ENCODING_GZIP = "gzip";

		protected void onProgressUpdate(Integer... progress) {
		}

		protected void onPostExecute(int result) {
		}

		@Override
		protected Integer doInBackground(String... urls) {
			parse(urls[0]);
			return null;
		}

		private InputStream getJSONStream(String url) {
			URL feedUrl = null;
			InputStream in = null;

			try {
				feedUrl = new URL(url);
			} catch (MalformedURLException e) {
				Log.e(LOG_TAG, "JSON url - " + feedUrl);
			}

			try {
				HttpUriRequest request = new HttpGet(feedUrl.toString());
				request.addHeader(HTTP_HEADER_ACCEPT_ENCODING,
						CONTENT_ENCODING_GZIP);
				final HttpResponse response = new DefaultHttpClient()
						.execute(request);
				Header ce = response
						.getFirstHeader(HTTP_HEADER_CONTENT_ENCODING);
				String contentEncoding = null;
				if (ce != null) {
					contentEncoding = ce.getValue();
				}
				in = response.getEntity().getContent();
				if (contentEncoding != null
						&& contentEncoding
								.equalsIgnoreCase(CONTENT_ENCODING_GZIP)) {
					in = new GZIPInputStream(in);
				}
			} catch (IOException e) {
				Log.e(e.getMessage(), "Error parsing: " + feedUrl);

			}

			return in;
		}

		/**
		 * @param url
		 */
		private void parse(String url) {

			InputStream instream = getJSONStream(url);
			if (instream == null) {
				
				runOnUiThread(new Runnable() {

			        @Override
			        public void run() {
			        	NotificationHelper.sendToast(
								DirectionsActivity.this,
								"Sorry, but no information from Google Maps Directions?\nAre you connected to the internet?",
								Toast.LENGTH_LONG);
			        }
			    });
 
				return;
			}

			// turn the stream into a string
			final String result = convertStreamToString(instream);
			// Create an empty route
			try {
				// Transform the string into a json object
				final JSONObject json = new JSONObject(result);
				// Get the route object
				final JSONObject jsonRoute = json.getJSONArray("routes")
						.getJSONObject(0);
				// Get the leg, only one leg as we don't support way points
				final JSONObject leg = jsonRoute.getJSONArray("legs")
						.getJSONObject(0);
				// Get the steps for this leg
				final JSONArray steps = leg.getJSONArray("steps");
				// Number of steps for use in for loop
				final int numSteps = steps.length();
				// Set the name of this route using the start & end addresses
				String name = (leg.getString("start_address") + " to " + leg
						.getString("end_address"));
				// Get google's copyright notice (tos requirement)
				String copyrignt = (jsonRoute.getString("copyrights"));
				// Get the total length of the route.
				int dist = (leg.getJSONObject("distance").getInt("value"));
				// Get any warnings provided (tos requirement)
				if (!jsonRoute.getJSONArray("warnings").isNull(0)) {
					String warning = (jsonRoute.getJSONArray("warnings")
							.getString(0));
				}

				GeoPoint startGP = myGeoPoint;
				mapView.getOverlays().add(
						new DrawRouteOverLay(startGP, startGP, 1));
				GeoPoint gp1;
				GeoPoint gp2 = startGP;

				/*
				 * This is a simple gathering of steps (route points), you can
				 * go into further details via the JSON object "polylines", see
				 * more in the Google Maps Directions documentation Loop through
				 * the steps, creating a segment for each one and decoding any
				 * polylines found as we go to add to the route object's map
				 * array.
				 */
				for (int i = 0; i < numSteps; i++) {

					// Get the individual step
					JSONObject step = steps.getJSONObject(i);
					// Set the length of this segment in metres

					JSONObject startLocation = step
							.getJSONObject("start_location");
					double startLatitude = startLocation.getDouble("lat");
					double startLongitude = startLocation.getDouble("lng");

					JSONObject endLocation = step.getJSONObject("end_location");
					double endLatitude = endLocation.getDouble("lat");
					double endLongitude = endLocation.getDouble("lng");

					JSONObject jsonDistance = step.getJSONObject("distance");
					final int distance = jsonDistance.getInt("value");
					final String distanceText = jsonDistance.getString("text");
					totalDistance += distance;

					String instruction = (step.getString("html_instructions")
							.replaceAll("<(.*?)*>", ""));

					Route route = new Route(startLatitude, startLongitude,
							instruction, distanceText, dist);
					routes.add(route);


					gp1 = gp2;
					// watch out! For GeoPoint, first:latitude, second:longitude
					gp2 = new GeoPoint((int) (endLatitude * 1E6),
							(int) (endLongitude * 1E6));
					mapView.getOverlays().add(
							new DrawRouteOverLay(gp1, gp2, 2, Color.BLUE));
				}

				directionsLoaded = true;

			} catch (JSONException e) {
				Log.e(e.getMessage(), "Error parsing JSON url - " + url);
			}

			mapView.getOverlays().add(
					new DrawRouteOverLay(targetGeoPoint, targetGeoPoint, 3));

			if (instream != null) {
				try {
					instream.close();
				} catch (IOException e) {
					Log.e(LOG_TAG, "Could not close inputstream", e);
				}
			}

			createFaceOverlays();
			setRouteInfo(0);
		}

		/**
		 * 
		 */
		private void createFaceOverlays() {
			Context context = DirectionsActivity.this;
			List<Overlay> mapOverlays = mapView.getOverlays();
			Resources resources = context.getResources();
			Drawable drawable = resources.getDrawable(R.drawable.ic_app);
			FaceItemizedOverlay myFaceOverlay = new FaceItemizedOverlay(
					drawable, context);
			OverlayItem myOverlayItem = new OverlayItem(myGeoPoint,
					PreferenceHelper.getFighterName(context),
					"Hey...It\'s me...Hi good looking!");

			Long contactId = PreferenceHelper.getTargetId(context);
			Bitmap photo = ContactsHelper.getPhoto(getContentResolver(),
					contactId);

			drawable = (photo != null) ? new BitmapDrawable(photo) : resources
					.getDrawable(R.drawable.icon_no_photo);
			FaceItemizedOverlay targetFaceOverlay = new FaceItemizedOverlay(
					drawable, context);

			OverlayItem targetOverlayItem = new OverlayItem(targetGeoPoint,
					"Target", "Take this target down!");

			myFaceOverlay.addOverlay(myOverlayItem);
			targetFaceOverlay.addOverlay(targetOverlayItem);

			mapOverlays.add(myFaceOverlay);
			mapOverlays.add(targetFaceOverlay);

		}

		/**
		 * Convert an inputstream to a string.
		 * 
		 * @param input
		 *            inputstream to convert.
		 * @return a String of the inputstream.
		 */

		private String convertStreamToString(final InputStream input) {
			final BufferedReader reader = new BufferedReader(
					new InputStreamReader(input));
			final StringBuilder sBuf = new StringBuilder();

			String line = null;
			try {
				while ((line = reader.readLine()) != null) {
					sBuf.append(line);
				}
			} catch (IOException e) {
				Log.e(e.getMessage(), "Google parser, stream2string");
			} finally {
				try {
					input.close();
				} catch (IOException e) {
					Log.e(e.getMessage(), "Google parser, stream2string");
				}
			}
			return sBuf.toString();
		}

	}

	@Override
	public void onResume() {
		super.onResume();
		mapView.postInvalidate();

		me.enableCompass();
	}

	@Override
	public void onPause() {
		super.onPause();

		me.disableCompass();
	}

	public void onConfigurationChanged(Configuration newConfig) {
		super.onConfigurationChanged(newConfig);
		mapView.invalidate();
	}

	@Override
	protected void onDestroy() {
		super.onDestroy();
		if (directionsLoader != null
				&& directionsLoader.getStatus() == AsyncTask.Status.RUNNING) {
			directionsLoader.cancel(true);
		}
	}

	
	private void centerMapBetweenMeAndTarget(MapRectangle mapRectangle) {
		MapController controller = mapView.getController();
		controller.zoomToSpan(mapRectangle.getLatitudeSpan(),
				mapRectangle.getLongitudeSpan());
		controller.animateTo(mapRectangle.getCenter());
	}
}
